package com.lincoln.framework.hibernate;

import com.lincoln.framework.spring.BeanUtils;
import com.lincoln.framework.spring.ReflectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.*;
import org.hibernate.criterion.*;
import org.hibernate.internal.CriteriaImpl;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.transform.ResultTransformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.util.*;

/**
 * Hibernate的范型基类.
 * 
 * 可以在service类中直接创建使用.也可以继承出DAO子类,在多个Service类中共享DAO操作.
 * 参考Spring2.5自带的Petlinc例子,取消了HibernateTemplate.
 * 通过Hibernate的sessionFactory.getCurrentSession()获得session,直接使用Hibernate原生API.
 *
 * @param <T> DAO操作的对象类型
 * @param <PK> 主键类型
 * 	
 * @author calvin
 */
@SuppressWarnings("unchecked")
public class SimpleHibernateTemplate<T, PK extends Serializable> {

	protected Logger logger = LoggerFactory.getLogger(getClass());

	protected SessionFactory sessionFactory;

	public static Session session;

	protected Class<T> entityClass;

	public SimpleHibernateTemplate() {
		this.entityClass = ReflectionUtils.getSuperClassGenricType(getClass());
	}
	
	public SimpleHibernateTemplate(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
		this.entityClass = ReflectionUtils.getSuperClassGenricType(getClass());
	}
	
	public SimpleHibernateTemplate(SessionFactory sessionFactory, Class<T> entityClass) {
		this.sessionFactory = sessionFactory;
		this.entityClass = entityClass;
	}
	
	/**
	 * 采用@Autowired按类型注入SessionFactory,当有多个SesionFactory的时候Override本函数.
	 */
	@Autowired
	public void setSessionFactory(final SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	public Session getSession() {
		if(session==null){
			session=sessionFactory.openSession();
		}
		return session;
	}

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}
	
	public void evict(T entity){
		getSession().evict(entity);
	}
	
	/**
	 * refresh
	 * @param entity
	 * @return
	 */
	public T refresh(T entity){
		Assert.notNull(entity);
		getSession().refresh(entity);
		return entity;
	}

	public void save(T entity) {
		Assert.notNull(entity);
		Session session = getSession();
		session.saveOrUpdate(entity);
		session.flush();
		logger.info("save entity: {}", entity);
	}

	public void saveOnly(T entity){
		try {
			Assert.notNull(entity);
			Session session = getSession();
			session.save(entity);
			session.flush();
			logger.info("saveOnly entity: {}", entity);
		}catch (Exception e){
			e.printStackTrace();
		}
	}

	public void delete(T entity) {
		Assert.notNull(entity);
		Session session = getSession();
		session.delete(entity);
		session.flush();
		logger.info("delete entity: {}", entity);
	}

	public void delete(PK id) {
		Assert.notNull(id);
		delete(get(id));
	}

	public List<T> findAll() {
		return findByCriteria();
	}

	public Page<T> findAll(Page<T> page) {
		return findByCriteria(page);
	}

	/**
	 * 按id获取对象.
	 */
	public T get(final PK id) {
		Session session = getSession();
		T t = session.get(entityClass, id);
		return t;
	}

	/**
	 * 按id获取对象.
	 */
	public T load(final PK id) {
		return (T) getSession().load(entityClass, id);
	}
	
	/**
	 * 按HQL查询对象列表.
	 * 
	 * @param hql hql语句
	 * @param values 数量可变的参数
	 */
	public List<T> find(String hql, Object... values) {
		return this.findLimit(-1, hql, values);
	}
	
	/**
	 * 按HQL查询对象列表.
	 * 
	 * @param values 命名参数,按名称绑定.
	 */
	public List<T> find(final String hql, final Map<String, Object> values) {
		return this.findLimit(-1, hql, values);
	}
	
	/**
	 * 按HQL查询前 max 条的对象列表.
	 * @param max 前 max 条，<=0 表示不限制
	 * @param hql hql语句
	 * @param values 数量可变的参数
	 */
	public List<T> findLimit(int max, String hql, Object... values) {
		return this.findLimit(createQuery(hql, values), max);
	}
	
	/**
	 * 按HQL查询对象列表.
	 * 
	 * @param values 命名参数,按名称绑定.
	 */
	public List<T> findLimit(int max, final String hql, final Map<String, Object> values) {
		return this.findLimit(createQuery(hql, values), max);
	}
	
	/**
	 * 数据查询，只做条数限制
	 * @param query
	 * @param max
	 * @return
	 */
	private  List<T> findLimit(Query query, int max) {
		if (max > 0) {
			query.setFirstResult(0);
			query.setMaxResults(max);
		}
		return query.list();
	}
	
	/**
	 * 执行HQL进行批量修改/删除操作.
	 */
	public int batchExecute(final String hql, final Object... values) {
		return createQuery(hql, values).executeUpdate();
	}

	/**
	 * 执行HQL进行批量修改/删除操作.
	 */
	public int batchExecute(final String hql, final Map<String, Object> values) {
		return createQuery(hql, values).executeUpdate();
	}
	
	/**
	 * 按HQL分页查询.
	 * 暂不支持自动获取总结果数,需用户另行执行查询.
	 * 
	 * @param page 分页参数.包括pageSize 和firstResult.
	 * @param hql hql语句.
	 * @param values 数量可变的参数，或者参数Map<String, Object>
	 * 
	 * @return 分页查询结果,附带结果列表及所有查询时的参数.
	 */
	public Page<T> find(Page<T> page, String hql, Object... values) {
		Assert.notNull(page);
		
		Map<String, Object> valueMap = null;
		boolean isValueMap = false;
		if (values != null && values.length > 0 && values[0] instanceof Map) {
			valueMap = (Map)values[0];
			isValueMap = true;
		}

		if (page.isAutoCount()) {
			long totalCount = isValueMap ? countHqlResult(hql, valueMap) : countHqlResult(hql, values);
			page.setTotalCount(totalCount);
		}
		
		this.resetCaculatePageNo(page);

		Query q = isValueMap ? createQuery(hql, valueMap) : createQuery(hql, values);
		if (page.isFirstSetted()) {
			q.setFirstResult(page.getFirst());
		}
		if (page.isPageSizeSetted()) {
			q.setMaxResults(page.getPageSize());
		}
		page.setResult(q.list());
		return page;
	}
	
	/**
	 * 执行count查询获得本次Hql查询所能获得的对象总数.
	 * 
	 * 本函数只能自动处理简单的hql语句,复杂的hql查询请另行编写count语句查询.
	 */
	protected long countHqlResult(final String hql, final Map<String, Object> values) {
		long count = 0;
		String fromHql = hql;
		//select子句与order by子句会影响count查询,进行简单的排除.
		fromHql = "from " + StringUtils.substringAfter(fromHql, "from");
		fromHql = StringUtils.substringBefore(fromHql, "order by");

		String countHql = "select count(*) " + fromHql;

		try {
			count = findLong(countHql, values);
		} catch (Exception e) {
			throw new RuntimeException("hql can't be auto count, hql is:" + countHql, e);
		}

		return count;
	}
	
	/**
	 * 执行count查询获得本次Hql查询所能获得的对象总数.
	 * 
	 * 本函数只能自动处理简单的hql语句,复杂的hql查询请另行编写count语句查询.
	 */
	protected long countHqlResult(final String hql, final Object... values) {
		long count = 0;
		String fromHql = hql;
		//select子句与order by子句会影响count查询,进行简单的排除.
		fromHql = "from " + StringUtils.substringAfter(fromHql, "from");
		fromHql = StringUtils.substringBefore(fromHql, "order by");

		String countHql = "select count(*) " + fromHql;

		try {
			count = findLong(countHql, values);
		} catch (Exception e) {
			throw new RuntimeException("hql can't be auto count, hql is:" + countHql, e);
		}
		return count;
	}

	/**
	 * 按HQL查询唯一对象.
	 */
	public Object findUnique(String hql, Object... values) {
		return createQuery(hql, values).uniqueResult();
	}

	/**
	 * 按HQL查询唯一对象.
	 * 
	 * @param values 命名参数,按名称绑定.
	 */
	public T findUnique(final String hql, final Map<String, Object> values) {
		return (T) createQuery(hql, values).uniqueResult();
	}
	
	/**
	 * 按HQL查询Intger类形结果. 
	 */
	public Integer findInt(String hql, Object... values) {
		return (Integer) findUnique(hql, values);
	}

	/**
	 * 按HQL查询Long类型结果. 
	 */
	public Long findLong(String hql, Object... values) {
		return (Long) findUnique(hql, values);
	}
	
	/**
	 * 按HQL查询Long类型结果. 
	 * @return
	 */
	public Long findLong(String hql, final Map<String, Object> values) {
		return (Long) findUnique(hql, values);
	}
	
	/**
	 * 按Criteria查询对象列表.
	 * @param criteria 条件对象
	 */
	public List<T> findByCriteria(Criteria criteria) {
		return criteria.list();
	}

	/**
	 * 按Criterion查询对象列表.
	 * @param criterion 数量可变的Criterion.
	 */
	public List<T> findByCriteria(Criterion... criterion) {
		return findByCriteria(createCriteria(criterion));
	}
	
	/**
	 * 按Criterion查询对象列表.
	 * @param criterion 排序对象
	 * @param criterion 数量可变的Criterion.
	 */
	public List<T> findByCriteria(Order order, Criterion... criterion) {
		return createCriteria(criterion).addOrder(order).list();
	}
	
	/**
	 * 重置当前页码，如果查询当前页大于此查询结果的最大分页数，设置当前页为最大分页数 //hht12.16
	 * 如果分页数量小于等于0，直接返回
	 */
	private void resetCaculatePageNo(Page page){
		if(page.pageSize <= 0) return;
		int maxPage =(int)((page.getTotalCount()+page.pageSize-1)/page.pageSize);
		if(page.getPageNo() > maxPage){
			page.setPageNo(maxPage);
		}
	}

	/**
	 * 按Criterion分页查询.
	 * @param page 分页参数.包括pageSize、firstResult、orderBy、asc、autoCount.
	 *             其中firstResult可直接指定,也可以指定pageNo.
	 *             autoCount指定是否动态获取总结果数.
	 *             
	 * @param criteria criteria
	 * @return 分页查询结果.附带结果列表及所有查询时的参数.
	 */
	public Page<T> findByCriteria(Page page, Criteria criteria) {
		if (page.hasAlias()) {
			for (Map.Entry<String, String> entry : page.getAlias().entrySet()) {
				criteria.createAlias(entry.getKey(), entry.getValue());
			}
		}
		
		if (page.isAutoCount()) {
			page.setTotalCount(countQueryResult(page, criteria));
		}
		if (page.isAutoCount() && page.getTotalCount() < 1){
			page.setResult(new ArrayList());
			return page;
		}
		
		this.resetCaculatePageNo(page);
		
		if (page.isFirstSetted()) {
			criteria.setFirstResult(page.getFirst());
		}
		if (page.isPageSizeSetted()) {
			criteria.setMaxResults(page.getPageSize());
		}

        if (page.isOrderBySetted()) {
			String[] orderByArray = StringUtils.split(page.getOrderBy(), ',');
			String[] orderArray = StringUtils.split(page.getOrder(), ',');

			Assert.isTrue(orderByArray.length == orderArray.length, "分页多重排序参数中,排序字段与排序方向的个数不相等");

			for (int i = 0; i < orderByArray.length; i++) {
				if (Page.ASC.equals(orderArray[i])) {
					criteria.addOrder(Order.asc(orderByArray[i]));
				} else {
					criteria.addOrder(Order.desc(orderByArray[i]));
				}
			}
		}

        /*
		if (page.isOrderBySetted()) {
			if (page.getOrder().endsWith(QueryParameter.ASC)) {
				c.addOrder(Order.asc(page.getOrderBy()));
			} else {
				c.addOrder(Order.desc(page.getOrderBy()));
			}
		}
         */
		page.setResult(criteria.list());
		return page;
	}
	
	/**
	 * 按Criterion分页查询.
	 * @param page 分页参数.包括pageSize、firstResult、orderBy、asc、autoCount.
	 *             其中firstResult可直接指定,也可以指定pageNo.
	 *             autoCount指定是否动态获取总结果数.
	 *             
	 * @param criterion 数量可变的Criterion.
	 * @return 分页查询结果.附带结果列表及所有查询时的参数.
	 */
	public Page<T> findByCriteria(Page page, Criterion... criterion) {
		Assert.notNull(page);

		Criteria c = createCriteria(criterion);
		
		return findByCriteria(page, c);
	}

	/**
	 * 按Criteria查询对象列表.
	 * 
	 * @param criterions 数量可变的Criterion.
	 */
	public List<T> find(final Criterion... criterions) {
		return createCriteria(criterions).list();
	}

	/**
	 * 按Criteria查询唯一对象.
	 * 
	 * @param criterions 数量可变的Criterion.
	 */
	public T findUnique(final Criterion... criterions) {
		return (T) createCriteria(criterions).uniqueResult();
	}

	/**
	 * 按属性查找对象列表,匹配方式为相等.
	 */
	public List<T> findBy(final String propertyName, final Object value) {
		Assert.hasText(propertyName, "propertyName不能为空");
		Criterion criterion = Restrictions.eq(propertyName, value);
		return find(criterion);
	}

	/**
	 * 按属性查找唯一对象,匹配方式为相等.
	 */
	public T findByUnique(final String propertyName, final Object value) {
		Assert.hasText(propertyName, "propertyName不能为空");
		Criterion criterion = Restrictions.eq(propertyName, value);
		return (T) createCriteria(criterion).uniqueResult();
	}
	
	/**
	 * 按属性查找对象列表.
	 * 
	 * @deprecated use findBy repleace
	 */
	public List<T> findByProperty(String propertyName, Object value) {
		Assert.hasText(propertyName);
		return createCriteria(Restrictions.eq(propertyName, value)).list();
	}

	/**
	 * 按属性查找唯一对象.
	 * @deprecated use findByUnique replace
	 */
	public T findUniqueByProperty(String propertyName, Object value) {
		Assert.hasText(propertyName);
		return (T) createCriteria(Restrictions.eq(propertyName, value)).uniqueResult();
	}

	/**
	 * 根据查询函数与参数列表创建Query对象,后续可进行更多处理,辅助函数.
	 */
	public Query createQuery(String queryString, Object... values) {
		Assert.hasText(queryString);
		Query queryObject = getSession().createQuery(queryString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				queryObject.setParameter(i, values[i]);
			}
		}
		return queryObject;
	}

	/**
	 * 根据查询HQL与参数列表创建Query对象.
	 * 
	 * @param values 命名参数,按名称绑定.
	 */
	public Query createQuery(final String queryString, final Map<String, Object> values) {
		Assert.hasText(queryString, "queryString不能为空");
		Query query = getSession().createQuery(queryString);
		if (values != null) {
			query.setProperties(values);
		}
		return query;
	}
	
	/**
	 * 根据Criterion条件创建Criteria,后续可进行更多处理,辅助函数.
	 */
	public Criteria createCriteria(Criterion... criterions) {
		Criteria criteria = getSession().createCriteria(entityClass);
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		return criteria;
	}

	/**
	 * 判断对象的属性值在数据库内是否唯一.
	 * 
	 * 在修改对象的情景下,如果属性新修改的值(value)等于属性原值(orgValue)则不作比较.
	 * 传回orgValue的设计侧重于从页面上发出Ajax判断请求的场景.
	 * 否则需要SS2里那种以对象ID作为第3个参数的isUnique函数.
	 */
	public boolean isPropertyUnique(String propertyName, Object newValue, Object orgValue) {
		if (newValue == null || newValue.equals(orgValue))
			return true;

		Object object = findByUnique(propertyName, newValue);
		return (object == null);
	}

	/**
	 * 通过count查询获得本次查询所能获得的对象总数.
	 * @return page对象中的totalCount属性将赋值.
	 */
	protected int countQueryResult(Page<T> page, Criteria c) {
		CriteriaImpl impl = (CriteriaImpl) c;

		// 先把Projection、ResultTransformer、OrderBy取出来,清空三者后再执行Count操作
		Projection projection = impl.getProjection();
		ResultTransformer transformer = impl.getResultTransformer();

		List<CriteriaImpl.OrderEntry> orderEntries = null;
		try {
			orderEntries = (List) BeanUtils.getFieldValue(impl, "orderEntries");
			BeanUtils.setFieldValue(impl, "orderEntries", new ArrayList());
		} catch (Exception e) {
			logger.error("不可能抛出的异常:{}", e.getMessage());
		}

		// 执行Count查询
		int totalCount = Integer.valueOf(c.setProjection(Projections.rowCount()).uniqueResult().toString());
		if (totalCount < 1)
			return 0; //-1修改为0

		// 将之前的Projection和OrderBy条件重新设回去
		c.setProjection(projection);

		if (projection == null) {
			c.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		}
		if (transformer != null) {
			c.setResultTransformer(transformer);
		}

		try {
			BeanUtils.setFieldValue(impl, "orderEntries", orderEntries);
		} catch (Exception e) {
			logger.error("不可能抛出的异常:{}", e.getMessage());
		}

		return totalCount;
	}
	
	/**
	 * 初始化对象.
	 * 使用load()方法得到的仅是对象Proxy后, 在传到View层前需要进行初始化.
	 * initObject(user) ,初始化User的直接属性，但不会初始化延迟加载的关联集合和属性.
	 * initObject(user.getRoles())，初始化User的直接属性和关联集合.
	 * initObject(user.getDescription())，初始化User的直接属性和延迟加载的Description属性.
	 */
	public void initObject(Object object) {
		Hibernate.initialize(object);
	}
	
	/**
	 * 批量初始化对象.
	 * @see #initObject(Object)
	 */
	public void initObjects(List list) {
		for (Object object : list) {
			Hibernate.initialize(object);
		}
	}
	
	/**
	 * 通过Set将不唯一的对象列表唯一化.
	 * 主要用于HQL/Criteria预加载关联集合形成重复记录,又不方便使用distinct查询语句时.
	 */
	public <X> List<X> distinct(List<X> list) {
		Set<X> set = new LinkedHashSet<X>(list);
		return new ArrayList<X>(set);
	}
	
	/**
	 * 取得对象的主键名.
	 */
	public String getIdName() {
		ClassMetadata meta = getSessionFactory().getClassMetadata(entityClass);
		Assert.notNull(meta, "Class " + entityClass.getSimpleName() + " not define in HibernateSessionFactory.");
		return meta.getIdentifierPropertyName();
	}
}